home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Nebula 2
/
Nebula Two.iso
/
Apps
/
ScreenSavers
/
BackSpaceViews
/
SchoolView.BackModule
/
SchoolView.m
< prev
next >
Wrap
Text File
|
1995-06-12
|
11KB
|
451 lines
/**********************************************************************
SchoolView.m
Author: David C. "Slam" Lambert
Date: 1 February, 1993
**********************************************************************/
#import <math.h>
#import <string.h>
#import <memory.h>
#import <appkit/Font.h>
#import <appkit/View.h>
#import <appkit/Button.h>
#import <appkit/NXImage.h>
#import <appkit/TextField.h>
#import <appkit/NXColorWell.h>
#import <defaults/defaults.h>
#import <dpsclient/wraps.h>
#import "Thinker.h"
#import "School.h"
#import "SchoolView.h"
#ifndef FONT_SIZE
#define FONT_SIZE (20.0)
#endif
static NXDefaultsVector SchoolViewDefaults =
{
{ "SchoolCount", "40" },
{ "SchoolDistExp", "3.0" },
{ "SchoolMomentum", "0.05" },
{ "SchoolMinRadius", "60.0" },
{ "SchoolMaxVelocity", "18.0" },
{ "SchoolMinVelocity", "0.0" },
{ "SchoolAccLimit", "5.0" },
{ "SchoolAvoidFact", "40" },
{ "SchoolMatchFact", "0.7" },
{ "SchoolCenterFact", "13" },
{ "SchoolTargetFact", "10" },
{ "SchoolFollowsMouse", "0" },
{ "SchoolChangeGoalFreq", "20"},
{ NULL },
};
static float
uniform(long seed)
{
long k;
long z;
static long s1;
static long s2;
if (seed != 0) {
s1 = seed;
s2 = ~seed;
}
k = s1 / 53668;
s1 = 40014 * (s1 - k * 53668) - k * 12211;
if (s1 < 0)
s1 = s1 + 2147483563;
k = s2 / 52774;
s2 = 40692 * (s2 - k * 52774) - k * 3791;
if (s2 < 0)
s2 = s2 + 2147483399;
z = s1 - s2;
if (z < 1)
z = z + 2147483562;
return(((float)z * 4.656613e-10));
}
@implementation SchoolView
+ initialize
{
NXRegisterDefaults([NXApp appName], SchoolViewDefaults);
return self;
}
- getDefaults
{
const char *theName = [NXApp appName];
schoolCount = atoi(NXGetDefaultValue(theName, "SchoolCount"));
maxVel = atof(NXGetDefaultValue(theName, "SchoolMaxVelocity"));
minVel = atof(NXGetDefaultValue(theName, "SchoolMinVelocity"));
distExp = atof(NXGetDefaultValue(theName, "SchoolDistExp"));
momentum = atof(NXGetDefaultValue(theName, "SchoolMomentum"));
accLimit = atof(NXGetDefaultValue(theName, "SchoolAccLimit"));
avoidFact = atof(NXGetDefaultValue(theName, "SchoolAvoidFact"));
matchFact = atof(NXGetDefaultValue(theName, "SchoolMatchFact"));
minRadius = atof(NXGetDefaultValue(theName, "SchoolMinRadius"));
centerFact = atof(NXGetDefaultValue(theName, "SchoolCenterFact"));
targetFact = atof(NXGetDefaultValue(theName, "SchoolTargetFact"));
followMouse = (BOOL)atoi(NXGetDefaultValue(theName, "SchoolFollowsMouse"));
goalChgFreq = atoi(NXGetDefaultValue(theName, "SchoolChangeGoalFreq"));
return self;
}
- writeDefaults
{
char string[100];
const char *theName = [NXApp appName];
sprintf(string, "%d", schoolCount);
NXWriteDefault(theName, "SchoolCount", string);
sprintf(string, "%.4f", maxVel);
NXWriteDefault(theName, "SchoolMaxVelocity", string);
sprintf(string, "%.4f", avoidFact);
NXWriteDefault(theName, "SchoolAvoidFact", string);
sprintf(string, "%d", followMouse);
NXWriteDefault(theName, "SchoolFollowsMouse", string);
return self;
}
- initFrame:(const NXRect *)frameRect
{
[super initFrame:frameRect];
[self setOpaque:YES];
[self setClipping:NO];
uniform(time(NULL));
theSchools = (BOID *)malloc(0);
oldString = malloc(0);
schoolString = malloc(0);
coords = (float *)malloc(0);
oldCoords = (float *)malloc(0);
[self getDefaults];
[self allocateGState];
[self setValues];
[self setup];
PSWDavesDefFont("Fish");
PSselectfont("Fish", FONT_SIZE);
return self;
}
- free
{
free(theSchools);
free(oldString);
free(schoolString);
free(coords);
free(oldCoords);
[targetImage free];
return [super free];
}
- setValues
{
[countField setIntValue:schoolCount];
[countSlider setIntValue:schoolCount];
[vMaxField setIntValue:maxVel];
[vMaxSlider setIntValue:maxVel];
[avoidField setIntValue:avoidFact];
[avoidSlider setIntValue:avoidFact];
[followSwitch setState:followMouse];
return self;
}
- awakeFromNib
{
[self setValues];
targetImage = [NXImage findImageNamed:"Target"];
distComp = FONT_SIZE * 0.707;
return self;
}
- takeValues:sender
{
schoolCount = [countSlider floatValue];
maxVel = [vMaxSlider floatValue];
avoidFact = [avoidSlider floatValue];
followMouse = [followSwitch state];
targetFact = (followMouse) ? 200.0 : 10.0;
[countField setIntValue:schoolCount];
[vMaxField setIntValue:maxVel];
[avoidField setIntValue:avoidFact];
[self setup];
[self writeDefaults];
return self;
}
- setup
{
int i;
int j;
BOID *b;
theSchools = realloc(theSchools, sizeof(BOID) * schoolCount);
oldString = (char *)realloc(oldString, sizeof(char) * (schoolCount + 1));
schoolString = (char *)realloc(schoolString, sizeof(char) * (schoolCount + 1));
coords = (float *)realloc(coords, sizeof(float) * schoolCount * 2);
oldCoords = (float *)realloc(oldCoords, sizeof(float) * schoolCount * 2);
bzero(coords, sizeof(float) * schoolCount * 2);
bzero(oldCoords, sizeof(float) * schoolCount * 2);
memset(oldString, 'A', schoolCount);
memset(schoolString, '0', schoolCount);
oldString[schoolCount] = schoolString[schoolCount] = '\0';
for(i = 0, j = -2, b = theSchools; i < schoolCount; i++, b++, j+=2) {
BOID_X(b) = NX_X(&frame) + uniform(0) * NX_WIDTH(&frame);
BOID_Y(b) = NX_Y(&frame) + uniform(0) * NX_HEIGHT(&frame);
BOID_XVEL(b) = 2.0 * (uniform(0)-0.5) * maxVel;
BOID_YVEL(b) = 2.0 * (uniform(0)-0.5) * maxVel;
BOID_XACC(b) = BOID_YACC(b) = 0.0;
if (i > 0) {
coords[j] = (BOID_X(b) - BOID_X(b-1));
coords[j+1] = (BOID_Y(b) - BOID_Y(b-1));
}
}
goalPoint.x = NX_MIDX(&frame); goalPoint.y = NX_MIDY(&frame);
if ([[self window] canStoreColor])
hasColor = YES;
[self display];
return self;
}
- inspector:sender
{
char buf[1024];
if (!thePanel) {
sprintf(buf, "%s/SchoolView.nib", [sender moduleDirectory:"School"]);
[NXApp loadNibFile:buf owner:self withNames:NO];
}
[self getDefaults];
return thePanel;
}
- (BOOL)ignoreMouseMovement
{
return followMouse;
}
- computeAccelerations
{
int i;
int j;
float cAx;
float cAy;
float aVx;
float aVy;
float dist;
float aMag;
float xDiff;
float yDiff;
float adjDist;
float adjDistSum;
BOID *b0;
BOID *b1;
NXPoint tmpPoint;
static unsigned counter;
adjDist = 0;
if (window != nil && followMouse) {
[window getMouseLocation:&tmpPoint];
if (NXMouseInRect(&tmpPoint, &frame, NO)) {
goalPoint = tmpPoint;
goalPoint.x -= 10.0; goalPoint.y -= 16.0;
}
}
else if (!((++counter) % goalChgFreq)) {
goalPoint.x = NX_MIDX(&frame) + (uniform(0)-0.5) * 0.45 * NX_WIDTH(&frame);
goalPoint.y = NX_MIDY(&frame) + (uniform(0)-0.5) * 0.45 * NX_HEIGHT(&frame);
}
/* other school avoidance */
for(i = 0, b0 = theSchools; i < schoolCount; i++, b0++) {
adjDistSum = 0.0;
cAx = cAy = aVx = aVy = 0.0;
BOID_XACC(b0) = BOID_YACC(b0) = 0.0;
for(j = 0, b1 = theSchools; j < schoolCount; j++, b1++) {
if (b1 == b0) continue;
xDiff = XDIFF(b0, b1);
yDiff = YDIFF(b0, b1);
if (xDiff > NX_MIDX(&frame))
xDiff = NX_MAXX(&frame) - xDiff;
if (yDiff > NX_MIDY(&frame))
yDiff = NX_MAXY(&frame) - yDiff;
dist = NORM(xDiff, yDiff) - distComp;
if (dist > minRadius) continue;
else if (dist <= 0.0) dist = MIN_DIST;
adjDist = dist * dist;
adjDistSum += (1.0 / adjDist);
xDiff /= adjDist; yDiff /= adjDist;
cAx -= xDiff; cAy -= yDiff;
BOID_XACC(b0) += xDiff; BOID_YACC(b0) += yDiff;
aVx += (BOID_XVEL(b1) / adjDist); aVy += (BOID_YVEL(b1) / adjDist);
}
xDiff = goalPoint.x - BOID_X(b0); yDiff = goalPoint.y - BOID_Y(b0);
BOID_XACC(b0) *= avoidFact; BOID_YACC(b0) *= avoidFact;
aMag = NORM(BOID_XACC(b0), BOID_YACC(b0));
/* velocity matching */
if (adjDistSum != 0.0 && aMag < accLimit) {
aVx /= adjDistSum; aVy /= adjDistSum;
BOID_XACC(b0) += ((aVx - BOID_XVEL(b0)) * matchFact);
BOID_YACC(b0) += ((aVy - BOID_YVEL(b0)) * matchFact);
aMag = NORM(BOID_XACC(b0), BOID_YACC(b0));
/* flock centering */
if (aMag < accLimit) {
BOID_XACC(b0) += cAx * centerFact;
BOID_YACC(b0) += cAy * centerFact;
aMag = NORM(BOID_XACC(b0), BOID_YACC(b0));
/* target attraction */
if (aMag < accLimit) {
BOID_XACC(b0) += xDiff * targetFact / adjDist;
BOID_YACC(b0) += yDiff * targetFact / adjDist;
}
}
}
BOID_XACC(b0) += (uniform(0)-0.5);
BOID_YACC(b0) += (uniform(0)-0.5);
aMag = NORM(BOID_XACC(b0), BOID_YACC(b0));
if (aMag > accLimit) {
BOID_XACC(b0) *= sqrt(accLimit/aMag);
BOID_YACC(b0) *= sqrt(accLimit/aMag);
}
}
return self;
}
- (BOOL)useBufferedWindow
{
return NO;
}
- didLockFocus
{
PSselectfont("Fish", FONT_SIZE);
return self;
}
- oneStep
{
int i;
int j;
int index;
float avgIndex;
BOID *b;
float vMag;
float oldX0 = BOID_X(theSchools);
float oldY0 = BOID_Y(theSchools);
NXColor theColor;
static float hue;
bcopy(coords, oldCoords, sizeof(float)*schoolCount*2);
bcopy(schoolString, oldString, sizeof(char)*schoolCount);
PSnewinstance();
PSsetinstance(YES);
if (followMouse)
[targetImage composite:NX_COPY toPoint:&goalPoint];
PSsetinstance(NO);
avgIndex = 0;
for(i = 0, j = -2, b = theSchools; i < schoolCount; i++, b++, j+=2) {
/* apply accelerations */
BOID_XVEL(b) = BOID_XACC(b) + (1.0 + momentum) * BOID_XVEL(b);
BOID_YVEL(b) = BOID_YACC(b) + (1.0 + momentum) * BOID_YVEL(b);
vMag = 1.0e-6 + NORM(BOID_XVEL(b), BOID_YVEL(b));
if (vMag > maxVel) {
BOID_XVEL(b) *= (maxVel/vMag);
BOID_YVEL(b) *= (maxVel/vMag);
}
else if (vMag < minVel) {
BOID_XVEL(b) *= (minVel/vMag);
BOID_XVEL(b) *= (minVel/vMag);
}
/* apply movements */
BOID_X(b) += BOID_XVEL(b);
BOID_X(b) = (BOID_X(b) > NX_MAXX(&frame)) ? 0.0 : BOID_X(b);
BOID_X(b) = (BOID_X(b) < NX_X(&frame)) ? NX_MAXX(&frame) : BOID_X(b);
BOID_Y(b) += BOID_YVEL(b);
BOID_Y(b) = (BOID_Y(b) > NX_MAXY(&frame)) ? 0.0 : BOID_Y(b);
BOID_Y(b) = (BOID_Y(b) < NX_Y(&frame)) ? NX_MAXY(&frame) : BOID_Y(b);
if (i > 0) {
coords[j] = (BOID_X(b) - BOID_X(b-1));
coords[j+1] = (BOID_Y(b) - BOID_Y(b-1));
}
if (fabs(fabs(BOID_XVEL(b))-fabs(BOID_YVEL(b))) < maxVel/2.0) {
if (BOID_XVEL(b) < 0.0)
index = (BOID_YVEL(b) > 0.0) ? '1' : '2';
else
index = (BOID_YVEL(b) > 0.0) ? '4' : '3';
}
else if (fabs(BOID_XVEL(b)) > fabs(BOID_YVEL(b)))
index = (BOID_XVEL(b) < 0.0) ? 'l' : 'r';
else
index = (BOID_YVEL(b) > 0.0) ? 'u' : 'd';
schoolString[i] = index;
}
NXSetColor(NX_COLORBLACK);
PSWDavesXYShow(oldX0, oldY0,
oldString, oldCoords, schoolCount*2);
if (hasColor) {
theColor = NXConvertHSBToColor(hue/360.0, 1.0, 1.0);
hue = (hue < 360.0) ? hue+1.0 : 0.0;
NXSetColor(theColor);
}
else
NXSetColor(NX_COLORWHITE);
PSWDavesXYShow(BOID_X(theSchools), BOID_Y(theSchools),
schoolString, coords, schoolCount*2);
[self computeAccelerations];
return self;
}
- (const char *)windowTitle
{
return "School by David Lambert";
}
- sizeTo:(NXCoord)width :(NXCoord)height
{
[super sizeTo:width :height];
[self setup];
return self;
}
- drawSelf:(const NXRect *)rects :(int)rectCount
{
if (!rects || !rectCount) return self;
PSselectfont("Fish", FONT_SIZE);
NXSetColor(NX_COLORBLACK);
NXRectFill(rects);
return self;
}
@end